
#include <cstdio>
#include "member_BSocket.h"
#include "ISockets.h"

using namespace BWE;

#ifdef linux
#include <fcntl.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#endif

member_BSocket::member_BSocket(BSocket* socket)
{
	App_SocketStart();
	boss = socket;
	sock = 0;
#ifdef _WIN32
	state = BSocket::State_Closed;
	error = BSocket::Error_None;
	errorStr = 0;
#endif
}
member_BSocket::~member_BSocket()
{
	App_SocketClean();
}

bool member_BSocket::create(int domain, int type, int protocol)
{
#ifdef linux
	sock = ::socket(domain, type, protocol);
	if (sock == -1)
	{
		sock = 0;
		perror("create socket");
		return false;
	}
	sockaddr_in addr;
	memset(&addr, 0, sizeof(addr));
	addr.sin_family = AF_INET;
	addr.sin_port = htons(567);
	addr.sin_addr.s_addr = inet_addr("127.0.0.1");
	int ret = ::bind(sock, (sockaddr*)&addr, sizeof(addr));
#endif
#ifdef _WIN32	
	sock = ::socket(domain, type, protocol);
	if (sock == INVALID_SOCKET)
	{
		perror("create socket failed.");
		return false;
	}
#endif
	return true;
}
bool member_BSocket::setnonblock()
{
#ifdef linux
	int opts = ::fcntl(sock, F_GETFL);
	if (opts == -1)
	{
		perror("fcntl(F_GETFL)");
		return false;
	}
	opts |= O_NONBLOCK;
	if (::fcntl(sock, F_SETFL, opts) == -1)
	{
		perror("fcntl(F_SETFL)");
		return false;
	}
#endif
#ifdef _WIN32	
	u_long mode = 0;
	int ret = ::ioctlsocket(sock, FIONBIO, &mode);
	if (ret == SOCKET_ERROR)
	{
		perror("ioctlsocket");
		return false;
	}
#endif
	return true;
}

bool member_BSocket::select(double seconds) const
{
	fd_set rfds;
	fd_set wfds;
	fd_set efds;

	FD_ZERO(&rfds);
	FD_ZERO(&wfds);
	FD_ZERO(&efds);

	FD_SET(socket_member(boss)->sock, &rfds);
	FD_SET(socket_member(boss)->sock, &wfds);
	FD_SET(socket_member(boss)->sock, &efds);

	timeval tv;
	if (seconds >= 0)
	{
		tv.tv_sec = (long)seconds;
		tv.tv_usec = (long)((seconds - tv.tv_sec) * 1000000);
	}

	int sock = (int)socket_member(this)->sock;
	int ret = ::select(sock, &rfds, &wfds, &efds, &tv);
	if (ret == -1)
	{
		perror("select");
		return false;
	}
	if (FD_ISSET(socket_member(this)->sock, &rfds))
		return true;
	if (FD_ISSET(socket_member(this)->sock, &wfds))
		return true;
	if (FD_ISSET(socket_member(this)->sock, &efds))
		return true;
	return false;
}

void member_BSocket::checkError()
{
#ifdef _WIN32	
	error = BSocket::Error_None;
	int errorCode = WSAGetLastError();
	switch (errorCode)
	{
	case WSAEBADF:
		errorStr = "The file handle supplied is not valid.";
		error = BSocket::Error_InvalidHandle;
		break;

	case WSAEADDRINUSE:
		errorStr = "Only one usage of each socket address (protocol/network address/port) is normally permitted.";
		error = BSocket::Error_MultipleUsage;
		break;

	case WSAEFAULT:
		errorStr = "The system detected an invalid pointer address in attempting to use a pointer argument in a call.";
		error = BSocket::Error_BadArgment;
		break;

	case WSAECONNREFUSED:
		errorStr = "No connection could be made because the target machine actively refused it";
		error = BSocket::Error_Refused;
		break;

	case WSAECONNRESET:
		errorStr = "An existing connection was forcibly closed by the remote host.";
		error = BSocket::Error_ClosedForcibly;
		break;

	case WSAEISCONN:
		errorStr = "Socket is already connected.";
		error = BSocket::Error_ConnectAgain;
		break;

	case WSAECONNABORTED:
		errorStr = "The virtual circuit was terminated due to a time - out or other failure.The application should close the socket as it is no longer usable.\
			This error applies only to a connection - oriented socket.";
		error = BSocket::Error_ConnectAgain;
		break;

	case WSAEINPROGRESS:
		errorStr = "A blocking Windows Sockets 1.1 call is in progress, or the service provider is still processing a callback function.";
		error = BSocket::Error_ConnectAgain;
		break;

	case WSAEINVAL:
		errorStr = "The how parameter is not valid, or is not consistent with the socket type.For example, SD_SEND is used with a UNI_RECV socket type.";
		error = BSocket::Error_ConnectAgain;
		break;

	case WSAEADDRNOTAVAIL:
		errorStr = "The requested address is not valid in its context.";
		error = BSocket::Error_InvalidAdrr;
		break;

	case WSAENETDOWN:
		errorStr = "The network subsystem has failed.";
		error = BSocket::Error_ConnectAgain;
		break;

	case WSAENOTCONN:
		errorStr = "The socket is not connected.This error applies only to a connection - oriented socket.";
		error = BSocket::Error_ConnectAgain;
		break;

	case WSAENOTSOCK:
		errorStr = "Note  The descriptor is not a socket.";
		error = BSocket::Error_ConnectAgain;
		break;

	case WSANOTINITIALISED:
		errorStr = "A successful WSAStartup call must occur before using this function.";
		error = BSocket::Error_ConnectAgain;
		break;

	case WSAEMSGSIZE:
		errorStr = "Read buffer size smaller than datagram.";
		error = BSocket::Error_BufferSmaller;
		break;

	default:
		break;
	}
#endif
	if (error != BSocket::Error_None)
	{
		boss->emit(Signal_Error, error);
	}
}

